package server;

import java.awt.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.List;

import constant.Constant;
import controller.RoomController;
import controller.protocol.AlternatingMessageProtocol;
import controller.protocol.GameProtocol;
import entity.*;
import entity.Event;
import entity.offer.Offer;
import sun.rmi.server.InactiveGroupException;
import ui.MessageFrame;
import util.SocketUtil;
import util.Time;
import util.Tuple2;

public class MyServer implements MessageHandler{
	
	private int port;
	private ServerSocket serverSocket;//
	private List<Socket> sockets;//
	private MessageFrame frame;
	private Map<Integer, Message> lastMessage;
	private Map<Integer, Object> messageLocks;
	private int connectedClient = 0;
	private int lastRoomNum;
	private List<NegoRoom> rooms;
	private Map<Integer, ServerMessage> servMsg;
	private Map<Integer, Object> servMsgLock;
	private RoomController roomController;
	
	public MyServer(int port){
		this.port = port;
	}
	
	public void start(){  
		
		try{
			serverSocket = new ServerSocket(port);
			System.out.println("Server is launched.");
			sockets = new ArrayList<>();
			lastMessage = new HashMap<>();
			messageLocks = new HashMap<>();
			rooms = new ArrayList<>();
			roomController = new RoomController(rooms);
			servMsg = new HashMap<>();
			servMsgLock = new HashMap<>();
			while(true){
				Socket client = serverSocket.accept();
				connectedClient++;
				if(frame == null){
					frame = new MessageFrame(serverSocket.getLocalSocketAddress().toString(), this);
				}
				System.out.println(client.getRemoteSocketAddress()+" logined in!");
				sockets.add(client);
				Tuple2<Integer, Integer> tuple2 = roomController.addClient(client);
				lastRoomNum = tuple2.get1();
				if(!lastMessage.containsKey(lastRoomNum)){
					lastMessage.put(lastRoomNum, new Message());
					messageLocks.put(lastRoomNum, new Object());
					servMsg.put(lastRoomNum, new ServerMessage());
					servMsgLock.put(lastRoomNum,new Object());
				}
				//lastRoomNum = (int)(connectedClient-1)/Constant.NUM_OF_PLAYER_EACH_ROOM;
				/*if(lastRoomNum>=rooms.size()){
					NegoRoom room = new NegoRoom(lastRoomNum);
					room.addClient(client);
					rooms.add(room);
					lastMessage.put(lastRoomNum, "");
					messageLocks.put(lastRoomNum, new Object());
					servMsg.put(lastRoomNum, new String(""));
					servMsgLock.put(lastRoomNum,new Object());
				}else{
					rooms.get(lastRoomNum).addClient(client);
				}*/
				handleSocket(client);
				Thread.sleep(100);
				//int thisNumOfClient = (connectedClient-1)%Constant.NUM_OF_PLAYER_EACH_ROOM;
				//int thisNumOfClient = tuple2.get2();
				//System.out.println("thisNumOfClient:  "+thisNumOfClient);
				//System.out.println("lastRoomNum:  "+lastRoomNum);
				//把房间当前在线的人数发送给房间内的每个客户端

				/*synchronized (servMsgLock.get(lastRoomNum)){
					//System.out.println("servMsg.get(lastRoomNum).notifyAll();");
					servMsgLock.get(lastRoomNum).notifyAll();
				}*/
			}
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	
	private void handleSocket(Socket socket){
		Thread handler = new SocketHandler(socket, this);
		handler.start();
	}

	@Override
	public void handleMessage(Message message) {
		// TODO Auto-generated method stub
		//System.out.println("rooms.size: "+rooms.get(0).getClients().size());
		int source = (int)message.getSource();
		int roomNum = (int)message.getDestination();
		rooms.get(roomNum).setLastSpeakerNum(source);
		//int msgRoomNum = findRoomNumBySocketAddr(from);
		message.setTime(Time.getHms());
		CounterMessage counterMessage = ((CounterMessage) message);
		counterMessage.setInfo(rooms.get(roomNum).generateRoomInfo());
		rooms.get(roomNum).getProtocol().setCounterMessage(counterMessage);
		counterMessage.setSpeakers(rooms.get(roomNum).getProtocol().nextSpeaker());
		lastMessage.put(roomNum, counterMessage);
		synchronized (messageLocks.get(roomNum)) {
			messageLocks.get(roomNum).notifyAll();
		}
		if(rooms.get(roomNum).getProtocol().isGameEnd()){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			String content = "协议达成，游戏已结束。";
			ServerMessage msg = new ServerMessage(this.serverSocket.getLocalSocketAddress().toString(), roomNum, content, Time.getHms(), Event.Nego_Finished, rooms.get(roomNum).generateRoomInfo());
			lastMessage.put(roomNum, msg);
			synchronized (messageLocks.get(roomNum)) {
				messageLocks.get(roomNum).notifyAll();
			}
		}
	}

	@Override
	public Message readMessage(String socketAddr) {
		// TODO Auto-generated method 
		Message message = null;
		int roomNum = 0;
		roomNum = findRoomNumBySocketAddr(socketAddr);
		//System.out.println(msgRoomNum);
		synchronized (messageLocks.get(roomNum)) {
			try {
				//System.out.println("before lastMessage.get(msgRoomNum).wait()");
				messageLocks.get(roomNum).wait();
				//System.out.println("after lastMessage.get(msgRoomNum).wait()");
				message = lastMessage.get(roomNum);
				/*System.out.println("source:     "+message.getSource());
				System.out.println("destination:     "+message.getDestination());
				System.out.println("body:       "+message.getBody());*/
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return message;
	}
	
	@Override
	public void socketHandlerEnded(Socket socket){
		/*String addr = socket.getRemoteSocketAddress().toString();
		int roomNum = findRoomNumBySocketAddr(addr);
		NegoRoom room = rooms.get(roomNum);*/
		try {
			roomController.clientExit(socket);
			int roomNum = findRoomNumBySocketAddr(SocketUtil.getSocketAddr(socket));
			NegoRoom room = roomController.getRoomByRoomNum(roomNum);
			if(room == null){
				messageLocks.remove(roomNum);
				lastMessage.remove(roomNum);
				servMsg.remove(roomNum);
				servMsgLock.remove(room);
				socket.close();
				return;
			}
			int clientNum = room.getClientNum(socket);
			String content = "第"+(clientNum + 1)+"个玩家已退出房间。";
			int amountOfClients = room.getOnlineClients();
			ServerMessage msg = new ServerMessage(this.serverSocket.getLocalSocketAddress().toString(), roomNum, content, Time.getHms(), Event.Client_Disconnect, room.generateRoomInfo());
			//sendMsgToRoom(roomNum, msg);
			lastMessage.put(roomNum, msg);
			synchronized (messageLocks.get(roomNum)){
				messageLocks.get(roomNum).notifyAll();
			}
			socket.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public void handleExtraInfo(Socket socket){
		int roomNum = findRoomNumBySocketAddr(socket.getRemoteSocketAddress().toString());
		//System.out.println("roomNum:  "+roomNum);
		//System.out.println("roomController:   "+roomController.getAmoutOfRooms());
		//num = this.rooms.get(roomNum).getClientNum(socket);
		int num = roomController.getRoomByRoomNum(roomNum).getClientNum(socket);
		//System.out.println("num:    "+num);
		//System.out.println(roomNum+"&  "+num);
		ServerMessage msg = new ServerMessage(this.serverSocket.getLocalSocketAddress().toString(), roomNum, String.valueOf(num), Time.getHms(), Event.Client_Connect, roomController.getRoomByRoomNum(roomNum).generateRoomInfo());
		sendMsgToClient(socket, msg);
		//System.out.println("msg:    "+msg.getBody());
		if(roomController.getRoomByRoomNum((roomNum)).getOnlineClients()==Constant.NUM_OF_PLAYER_EACH_ROOM){
			msg.setBody("第"+(num+1)+"个玩家已上线，游戏开始。");
			msg.setEvent(Event.Nego_Start);
		}else{
			msg.setBody("第"+(num+1)+"个玩家已上线。");
		}
		lastMessage.put(roomNum, msg);
		synchronized (messageLocks.get(roomNum)){
			messageLocks.get(roomNum).notifyAll();
		}
	}

	public void sendMsgToRoom(int roomNum, Message msg){
		for(Socket socket: rooms.get(roomNum).getClients()){
			if(socket!=null&&!socket.isClosed()){
				sendMsgToClient(socket, msg);
			}
		}
	}

	public void sendMsgToClient(Socket socket, Message msg){
		try{
			/*DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
			dos.writeUTF(msg);
			dos.flush();*/
			ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
			//oos.reset();
			oos.writeObject(msg);
			//oos.flush();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

	private int findRoomNumBySocketAddr(String socketAddr){
		int num = 0;
		for(NegoRoom room: rooms){
			if(room.isClientInThisRoom(socketAddr)){
				num = room.getRoomNum();
				break;
			}
		}
		return num;
	}
	
	
}
